package cd.safe.school.util;

import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.widget.ProgressBar;
import cd.safe.school.data.DownloadDbHelper;

public class MultiThreadDownload {
	private String url;
	// 线程数
	private Integer maxThread = 5;

	private String fileName;
	private File file;
	private DownloadDbHelper dbHelper;
	private SQLiteDatabase db;
	private boolean stop = false;
	private ProgressBar progressBar;
	// 记录已经下载的字节数
	private Integer totalProgress = 0;

	public MultiThreadDownload(Context context, String url) {
		super();
		this.url = url;
		fileName = url.substring(url.lastIndexOf('/') + 1);
		file = new File(Environment.getExternalStorageDirectory(), "download/"
				+ fileName);
		if (!file.getParentFile().exists())
			file.getParentFile().mkdirs();
		dbHelper = new DownloadDbHelper(context);
		db = dbHelper.getWritableDatabase();
	}

	public void setMaxThread(Integer maxThread) {
		this.maxThread = maxThread;
	}

	public void setProgressBar(ProgressBar progressBar) {
		this.progressBar = progressBar;
	}

	public void stop() {
		// 1、停止刚才运行的线程
		// Thread.interrupt()存在很大的不确定性/设置控制变量
		stop = true;
	}

	public void continueDownload(Cursor cursor) {
		if (progressBar != null) {
			// 设置下载的总字节,断点下载时，以前下载文件已经存在，可以通过读文件大小直接得到下载的字节数
			progressBar.setMax((int) file.length());
			// 还需要下载字节数的总和
			Cursor c = db
					.rawQuery(
							"select sum(vend-vbegin-progress) from download where url=?",
							new String[] { url });
			if (c.moveToNext()) {
				int balance = c.getInt(0);
				// 获得当前已经下载的总字节数
				totalProgress = progressBar.getMax() - balance;
				progressBar.setProgress(totalProgress);
			}
			c.close();

		}
		System.out.println("断点下载....");
		while (cursor.moveToNext()) {
			// 读取下载进度信息
			int threadNo = cursor.getInt(cursor.getColumnIndex("threadNo"));
			int begin = cursor.getInt(cursor.getColumnIndex("vbegin"));
			int end = cursor.getInt(cursor.getColumnIndex("vend"));
			int progress = cursor.getInt(cursor.getColumnIndex("progress"));
			// 创建下载任务
			DownloadTask dt = new DownloadTask(threadNo, begin, end, progress);
			new Thread(dt).start();
		}
		cursor.close();
	}

	public void download() {
		// 查询数据库中有没针对当前url的下载任务
		Cursor cursor = db.rawQuery("select * from download where url=?",
				new String[] { url });
		if (cursor.getCount() > 0) {
			// 断点下载
			continueDownload(cursor);
			return;
		}
		// 1、先获得要下载的资源的总大小,通过Content-Length的响应头获得
		try {
			URL u = new URL(url);
			int total = u.openConnection().getContentLength();
			if (progressBar != null) {
				progressBar.setMax(total);
			}
			System.out.println("文件总大小:" + total);
			// 使用RandomAccessFile类来创建一个大小为total的空文件
			RandomAccessFile raf = new RandomAccessFile(file, "rwd");
			// 指定大小
			raf.setLength(total);
			raf.close();
			// 2，计算每个线程下载大小及具体起始字节
			int perSize = total / maxThread;
			for (int i = 0; i < maxThread; i++) {
				// 计算出每一个线程下载起始字节数
				int begin = i * perSize;
				int end = begin + perSize;
				if (i == maxThread - 1) {
					// 最后一个的end要到文件结束
					end = total;
				}
				// 开线程，并控制其去下载他所该要下载的部分
				DownloadTask dt = new DownloadTask(i, begin, end);
				// 在数据库中保存下载任务
				String sql = "insert into download (url,threadNo,vbegin,vend,progress) values(?,?,?,?,?)";
				db.execSQL(sql, new Object[] { url, i, begin, end, 0 });
				new Thread(dt).start();
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private class DownloadTask implements Runnable {
		// 线程编号
		private int threadNo;
		private int begin;
		private int end;
		private int progress;

		public DownloadTask(int threadNo, int begin, int end, int progress) {
			super();
			this.threadNo = threadNo;
			this.begin = begin;
			this.end = end;
			this.progress = progress;
		}

		public DownloadTask(int threadNo, int begin, int end) {
			super();
			this.threadNo = threadNo;
			this.begin = begin;
			this.end = end;
		}

		@Override
		public void run() {
			// 去下载具体的字节数
			try {
				URL u = new URL(url);
				HttpURLConnection conn = (HttpURLConnection) u.openConnection();
				System.out.println("线程" + threadNo + "开始下载....");
				// 告诉服务器端只需要发从begin到end-1个字节
				conn.setRequestProperty("Range", "bytes=" + (begin + progress)
						+ "-" + (end - 1));
				InputStream in = conn.getInputStream();
				// 从InputStream中把内容读出来，并写到文件中
				// 写到sdcard上
				// 1、每一个线程写一个独立的片段文件
				/*
				 * File file=new
				 * File(Environment.getExternalStorageDirectory(),"download/"
				 * +fileName+"_"+threadNo+".tmp");
				 * if(!file.getParentFile().exists
				 * ())file.getParentFile().mkdirs(); OutputStream out=new
				 * FileOutputStream(file);
				 */
				RandomAccessFile raf = new RandomAccessFile(file, "rwd");
				// 先跳过begin个字节，再开始写
				raf.skipBytes(begin + progress);
				// 准备缓冲
				byte[] buffer = new byte[1024];
				int len = -1;
				while ((len = in.read(buffer)) > 0) {
					// 当调用RandomAccessFile的write方法会立即把内容写到文件中
					raf.write(buffer, 0, len);
					// 记录下载进度
					progress += len;
					// 需要进行下载的总进度显示
					if (progressBar != null) {
						synchronized (totalProgress) {
							// 只有一个线程能进行这个代码块
							totalProgress += len;
						}
						progressBar.setProgress(totalProgress);
					}
					// 把下载的字节数写到数据库中
					db.execSQL(
							"update download set progress=? where url=? and threadNo=?",
							new Object[] { progress, url, threadNo });
					System.out.println("线程：" + threadNo + " progress:"
							+ progress);
					// 看看是否要结束线程
					if (stop)
						return;
				}
				// out.close();
				raf.close();
				in.close();
				// 下载完成删除下载任务
				db.execSQL("delete from download where url=? and threadNo=?",
						new Object[] { url, threadNo });
				System.out.println("线程" + threadNo + "结束.");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}
}
